Skip to content

Conversation

@gzproger
Copy link

@gzproger gzproger commented Jan 8, 2026

Changes

  • Rewrote ProcessLockfile to iterate ALL packages directly (matching npm's actual behavior)
  • Applied PackageJsonEnginesConverter to PackageLockV3Package.Engines (PR don't throw if engines property is a string array #1400 only fixed NpmComponentDetector, not NpmLockfile3Detector)
  • Fixed devOptional classification for packages that are both dev and optional dependencies of non-dev packages
  • Added node-style module resolution for building dependency graph edges

Testing

  • All unit tests pass (11/11)
  • Tested on 3 real-world repos with correct component counts

…cation

- Rewrite ProcessLockfile to iterate ALL packages directly instead of tree traversal
- Add node-style module resolution for dependency graph edges
- Fix devOptional and peer+dev dependencies to be classified as dev dependencies
- Skip link packages (symbolic links) and bundled dependencies
- Handle engines field as JsonElement to support both object and array formats
- Add tests for devOptional, peer-only, and engines-as-array scenarios

Fixes issue microsoft#1380

## Test Results

Tested against multiple real-world repositories with npm lockfile v3:
- Main branch crashes when engines field is array format instead of object
- This PR correctly handles both object and array formats for engines
- This PR detects all packages from lockfile including nested dependencies
- NpmLockfile3 now finds the complete transitive dependency tree
@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

❌ Patch coverage is 95.59939% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.2%. Comparing base (2ed2e3b) to head (36b9efe).

Files with missing lines Patch % Lines
...entDetection.Detectors/npm/NpmLockfile3Detector.cs 80.0% 12 Missing and 9 partials ⚠️
...onentDetection.Detectors.Tests/NpmTestUtilities.cs 90.2% 0 Missing and 8 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##            main   #1606     +/-   ##
=======================================
+ Coverage   90.1%   90.2%   +0.1%     
=======================================
  Files        437     437             
  Lines      37431   38027    +596     
  Branches    2316    2342     +26     
=======================================
+ Hits       33736   34319    +583     
+ Misses      3221    3219      -2     
- Partials     474     489     +15     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes nested dependency detection in NpmLockfile3Detector and applies the missing engines array converter fix from PR #1400. The core change rewrites the ProcessLockfile method from a queue-based traversal to a cleaner three-pass approach that processes all packages directly, then registers components, and finally builds dependency edges using node-style module resolution.

Key changes:

  • Replaces complex queue-based dependency traversal with direct iteration over all packages in lockfile
  • Implements node-style module resolution for dependency graph construction, walking up the directory tree to find dependencies
  • Fixes devOptional classification to correctly mark peer dependencies that are also dev dependencies

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Microsoft.ComponentDetection.Detectors/npm/NpmLockfile3Detector.cs Complete rewrite of ProcessLockfile to use three-pass approach with node-style resolution; removes queue-based traversal
src/Microsoft.ComponentDetection.Detectors/npm/NpmLockfileDetectorBase.cs Adds new RecordComponent overload for registering components with explicit reference flag
src/Microsoft.ComponentDetection.Detectors/npm/Contracts/PackageLockV3Package.cs Applies PackageJsonEnginesConverter to Engines property to handle array format
test/Microsoft.ComponentDetection.Detectors.Tests/NpmLockfile3DetectorTests.cs Adds three new tests for devOptional, peer, and engines array handling
test/Microsoft.ComponentDetection.Detectors.Tests/NpmTestUtilities.cs Adds helper methods for generating test lockfiles with devOptional and peer dependencies

Copilot AI review requested due to automatic review settings January 12, 2026 18:44
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Comment on lines +782 to +784
// The shared dependency appears in both dev and non-dev contexts, so should NOT be marked as dev
var sharedDep = detectedComponents.First(x => ((NpmComponent)x.Component).Name.Equals(sharedDepName));
componentRecorder.GetEffectiveDevDependencyValue(sharedDep.Component.Id).Should().BeFalse();
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test comment states "The shared dependency appears in both dev and non-dev contexts," but the lockfile structure shows the shared dependency appears only once at "node_modules/{4}" without a dev flag. The multi-path dev status tracking logic (lines 135-138 in NpmLockfile3Detector.cs) is not actually exercised by this test since the component appears at only one path in the lockfile. Consider adding a test case where the same component (name + version) appears at multiple different paths in the lockfile with different dev flags to properly test the multi-path logic.

Copilot uses AI. Check for mistakes.
@gzproger
Copy link
Author

@copilot open a new pull request to apply changes based on the comments in this thread

@gzproger gzproger closed this Jan 12, 2026
@gzproger gzproger reopened this Jan 12, 2026
Copilot AI review requested due to automatic review settings January 12, 2026 18:55
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


// Build package lookup - keys are paths like "node_modules/lodash" or "node_modules/a/node_modules/b"
// Collect direct dependencies from package.json for explicit reference tracking
var directDependencies = new HashSet<string>(System.StringComparer.Ordinal);
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NPM package names are case-sensitive according to the npm specification. Using StringComparer.OrdinalIgnoreCase could lead to incorrect matching of package names. For example, "React" and "react" would be treated as the same package when they are actually different. This should be changed to StringComparer.Ordinal to ensure case-sensitive matching.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants